package domain ;

import java.util.Set ;

import org.jasypt.util.password.StrongPasswordEncryptor ;

import persistence.CorruptDataException ;
import persistence.PersistenceFactory ;
import util.Bug ;
import util.DuplicationException ;
import util.HierarchicalSet ;
import util.Log ;
import util.NotFoundException ;
import util.Subsystem ;
import domain.message.Directory ;
import domain.message.MessageServer ;
import domain.user.Member ;
import domain.user.Privilege ;
import domain.user.UserServer ;

/**
 * the repository for the forum. holds the userServer which holds and stores all the users in the system, and
 * the root of the messages tree of the forum system.
 *
 * being manipulated by both controllers: UserController and MessageController.
 */
public class ForumData
{
	private static final String ADMIN_INITIAL_ENCRYPTED_PASSWORD = new StrongPasswordEncryptor().encryptPassword("12") ;
	private static final String ADMIN_INITIAL_USERNAME = "admin" ;
	private static final Set<Privilege> ALL_PRIVILEGES = new HierarchicalSet<Privilege>(
		Privilege.ADD_DIRECTORY,
		Privilege.ADD_POST,
		Privilege.ADD_THREAD,
		Privilege.DELETE_DIRECTORY,
		Privilege.DELETE_POST,
		Privilege.DELETE_THREAD,
		Privilege.DENY_PERMISSION,
		Privilege.EDIT_POST,
		Privilege.EDIT_ANY_POST,
		Privilege.GIVE_PERMISSION,
		Privilege.MOVE_DIRECTORY,
		Privilege.MOVE_POST,
		Privilege.MOVE_THREAD,
		Privilege.REPLY_POST,
		Privilege.VIEW_DIRECTORY,
		Privilege.VIEW_POST,
		Privilege.VIEW_THREAD,
		Privilege.VIEW_STATISTICS,
		Privilege.EDIT_PROFILE
	) ;
	private static final long ROOT_DIRECTORY_ID = 1 ;
	private static final String ADMIN_NAME = ForumData.ADMIN_INITIAL_USERNAME ;
	private static final String ROOT_DIRECTORY_NAME = "root" ;

	private Member admin ;
	private Directory root ;
	private UserServer users ;
	private StatisticsServer statistics ;
	private MessageServer messages;

	/**
	 * @param persistenceGenerator persistence provider for the forum's data.
	 * @throws CorruptDataException if the provider provided corrupt data.
	 */
	public ForumData(final PersistenceFactory persistenceGenerator) throws CorruptDataException
	{
		this.initializeServers(persistenceGenerator) ;
		this.setInitialForumElements(persistenceGenerator.loadingFromExisting()) ;
	}

	/**
	 * shut down the forum.
	 * @post all forum info is in the persistent database.
	 */
	public void close()
	{
		Log.getLogger(Subsystem.SERVICES).warning("shutting down forum") ;
		this.getMessages().close() ;
		this.getUsers().close() ;
	}

	/**
	 * @return administrator of this forum.
	 */
	public Member getAdmin()
	{
		return this.admin ;
	}

	/**
	 * @return the messages repository
	 */
	public MessageServer getMessages()
	{
		return this.messages ;
	}

	/**
	 * @return the directories tree root.
	 */
	public Directory getRoot()
	{
		return this.root ;
	}

	/**
	 * @return statistics repository.
	 */
	public StatisticsServer getStatistics()
	{
		return this.statistics ;
	}

	/**
	 * @return the users repository.
	 */
	public UserServer getUsers()
	{
		return this.users ;
	}

	private void initializeServers(final PersistenceFactory persistenceGenerator) throws CorruptDataException
	{
		Log.getLogger(Subsystem.SERVICES).config("initializing servers") ;
		this.users = new UserServer(persistenceGenerator) ;
		this.messages = new MessageServer(persistenceGenerator) ;
		this.statistics = new StatisticsServer(persistenceGenerator) ;
	}

	private void setInitialForumElements(final boolean loadOnConstruction) throws CorruptDataException
	{
		if (loadOnConstruction)
		{
			try
			{
				Log.getLogger(Subsystem.SERVICES).config("loading directory tree") ;
				this.root = this.messages.getDirectory(ForumData.ROOT_DIRECTORY_ID) ;
			}
			catch (final NotFoundException e)
			{
				throw new CorruptDataException("root directory not found") ;
			}

			this.admin = this.users.getMember(ForumData.ADMIN_NAME) ;
			if (this.admin == null)
			{
				throw new CorruptDataException("administrator not found") ;
			}
		}
		else
		{
			Log.getLogger(Subsystem.SERVICES).config("building administration structure") ;
			try
			{
				this.admin = this.users.createMember(ForumData.ADMIN_INITIAL_USERNAME,
					ForumData.ADMIN_INITIAL_ENCRYPTED_PASSWORD, ForumData.ALL_PRIVILEGES) ;
			}
			catch (final DuplicationException e)
			{
				throw new Bug("duplicate adminstrator", e) ;
			}
			this.root = this.messages.createDirectory(ForumData.ROOT_DIRECTORY_NAME) ;
			if (this.root.getId() != ForumData.ROOT_DIRECTORY_ID)
			{
				throw new CorruptDataException("root directory id is not as expected (" +
					this.root.getId() + " instead of " + ForumData.ROOT_DIRECTORY_ID + ")") ;
			}
		}
	}

}
